package org.fhm.zdte.client.service;

import org.fhm.substrate.annotation.Component;
import org.fhm.substrate.annotation.Setup;
import org.fhm.substrate.service.LoggerHandler;
import org.fhm.substrate.standard.ILogger;
import org.fhm.substrate.standard.IStarter;
import org.fhm.zdte.client.api.ClientApi;
import org.fhm.zdte.client.config.PermissionConfiguration;
import org.fhm.zdte.client.constant.ChecksumFreeFlags;
import org.fhm.zdte.client.constant.EnableMode;
import org.fhm.zdte.client.standard.IRequestProxy;
import org.fhm.zdte.common.annotation.InternalProvider;
import org.fhm.zdte.common.annotation.Provider;
import org.fhm.zdte.common.constant.CommonConstant;
import org.fhm.zdte.common.exception.ServletException;
import org.fhm.zdte.common.pojo.Address;
import org.fhm.zdte.common.pojo.Request;
import org.fhm.zdte.common.pojo.Result;
import org.fhm.zdte.common.standard.IInterfaceScheduler;
import org.fhm.zdte.common.standard.IJsonConvertor;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * The client side implementation of JSubstrate's {@link IStarter}.
 *
 * @author 谭波
 * @since 2023/11/22
 */
@Component
public class ClientStarter extends AbstractClientStarter {

    private final ILogger logger = LoggerHandler.getLogger(ClientApi.class);

    @Setup
    private IJsonConvertor iJsonConvertor;

    @Setup("INTERNAL")
    private IInterfaceScheduler internalScheduler;

    @Setup("OUTWARD")
    private IInterfaceScheduler outwardScheduler;

    @Setup
    private PermissionConfiguration permissionConfiguration;

    @Setup
    private IRequestProxy iRequestProxy;

    @Setup
    private ClientApi clientApi;

    @Override
    public boolean isRefuseConnect(Address isa, String secretKey) {
        String allowConnectedSecretKey;
        String allowConnectedAddresses;
        String separator = CommonConstant.PACKAGE_SEQUENCE_NUMBER_SEPARATOR.name();
        return (
                Objects.nonNull((allowConnectedAddresses = permissionConfiguration
                        .getAllowConnectedAddresses()))
                        &&
                        !allowConnectedAddresses.equals(ChecksumFreeFlags.FILTER_FREE_ADDRESSES.getName())
                        &&
                        !Arrays.asList(
                                        allowConnectedAddresses
                                                .split(separator))
                                .contains(isa.getIpv4())
        )
                ||
                (
                        Objects.nonNull((allowConnectedSecretKey = permissionConfiguration
                                .getAllowConnectedSecretKey()))
                                &&
                                !allowConnectedSecretKey.equals(ChecksumFreeFlags.FILTER_FREE_SECRET_KEY.getName())
                                &&
                                !Arrays.asList(
                                                allowConnectedSecretKey
                                                        .split(separator))
                                        .contains(secretKey)
                );

    }

    @Override
    public List<Class<? extends Annotation>> newManageMembers() {
        List<Class<? extends Annotation>> annotations = new ArrayList<>();
        annotations.add(InternalProvider.class);
        annotations.add(Provider.class);
        return annotations;
    }

    @Override
    public void manageNotify(List<?> beans, Class<? extends Annotation> clazz) {
        try {
            if (Provider.class.isAssignableFrom(clazz)) {
                outwardScheduler.mappingInterfaceWithMethods(beans);
            } else {
                internalScheduler.mappingInterfaceWithMethods(beans);
            }
        } catch (ServletException e) {
            logger.error(e.getMessage(), e);
            try {
                this.close();
            } catch (Exception ignore) {
            }
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Override
    public Result<String> dispatchServlet(String from, Request bean) {
        Result<String> result;
        try {
            result = Result.reSetResult(
                    outwardScheduler.invokedMethodByInterfaceUrl(
                            from, bean.getInterfaceUrl(), bean.getParams()
                    ),
                    content -> iJsonConvertor.toJson(content)
            );
        } catch (Exception e) {
            result = Result.getFailResult("", e.getMessage());
        }
        return result;
    }

    @Override
    public Result<?> dispatchInternalServlet(Request bean) {
        Result<?> result;
        try {
            result = Result.reSetResult(internalScheduler.invokedMethodByInterfaceUrl(
                    "", bean.getInterfaceUrl(), bean.getParams()
            ), content -> iJsonConvertor.toJson(content));
        } catch (Exception e) {
            return Result.getFailMessage(e.getMessage());
        }
        return result;
    }

    @Override
    public void start(String[] args) throws Exception {
        String property = System.getProperty("permission.enable.mode");
        String mode = "SERVICE";
        String proxy = EnableMode.PROXY.name();
        if (Objects.isNull(property) || property.isEmpty()) {
            if (Objects.nonNull(iRequestProxy) && EnableMode.isProxy(permissionConfiguration.getEnableMode()))
                mode = proxy;
        } else {
            logger.info("the current VM configuration mode is {}", property);
            if (EnableMode.isProxy(property)) {
                mode = proxy;
            }
        }
        if (proxy.equals(mode)) {
            logger.info("agent mode starts");
            iRequestProxy.initial().enable();
        } else {
            logger.info("service mode starts");
            clientApi.clientOnline();
        }
    }

    @Override
    public void close() throws Exception {
        super.close();
        if (Objects.nonNull(iRequestProxy) && EnableMode.isProxy(permissionConfiguration.getEnableMode())) {
            iRequestProxy.close();
        }
    }

}
